<?

/**
 * Representation of an error page element for a web application,
 * as represented in a <code>&lt;error-page&gt;</code> element in the
 * deployment descriptor.
 */

class ErrorPage {


    // ----------------------------------------------------- Instance Variables


    /**
     * The error (status) code for which this error page is active.
     */
    private $errorCode = 0;


    /**
     * The exception type for which this error page is active.
     */
    private $exceptionType = null;


    /**
     * The context-relative location to handle this error or exception.
     */
    private $location = null;


    // ----------------------------------------------------------- Constructors


    public function __construct($code, $location, $exceptionType = null) {
        $this->errorCode = $code;
        $this->location = $location;
        $this->exceptionType = $exceptionType;
    }


    // ------------------------------------------------------------- Properties


    /**
     * Return the error code.
     */
    public function getErrorCode() {

        return ($this->errorCode);

    }


    /**
     * Set the error code.
     *
     * @param errorCode The new error code
     */
    public function setErrorCode($errorCode) {

        $this->errorCode = intval($errorCode);

    }


    /**
     * Return the exception type.
     */
    public function getExceptionType() {

        return ($this->exceptionType);

    }


    /**
     * Set the exception type.
     *
     * @param exceptionType The new exception type
     */
    public function setExceptionType($exceptionType) {

        $this->exceptionType = $exceptionType;

    }


    /**
     * Return the location.
     */
    public function getLocation() {

        return ($this->location);

    }


    /**
     * Set the location.
     *
     * @param location The new location
     */
    public function setLocation($location) {

        $this->location = urldecode($location);

    }


    // --------------------------------------------------------- Public Methods


    /**
     * Render a String representation of this object.
     */
    public function __toString() {

        $sb = "ErrorPage[";
        if ($this->exceptionType == null) {
            $sb .= "errorCode=";
            $sb .= $this->errorCode;
        } else {
            $sb .= "exceptionType=";
            $sb .= $this->exceptionType;
        }
        $sb .= ", location=";
        $sb .= $this->location;
        $sb .= "]";
        return ($sb);

    }


}


/**
 * Representation of a login configuration element for a web application,
 * as represented in a <code>&lt;login-config&gt;</code> element in the
 * deployment descriptor.
 */
class LoginConfig {


    // ----------------------------------------------------------- Constructors

    /**
     * Construct a new LoginConfig with the specified properties.
     *
     * @param authMethod The authentication method
     * @param realmName The realm name
     * @param loginPage The login page URI
     * @param errorPage The error page URI
     */
    public function __construct($authMethod = null, $realmName = null,
                       $loginPage = null, $errorPage = null) {

        $this->setAuthMethod($authMethod);
        $this->setRealmName($realmName);
        $this->setLoginPage($loginPage);
        $this->setErrorPage($errorPage);

    }


    // ------------------------------------------------------------- Properties


    /**
     * The authentication method to use for application login.  Must be
     * BASIC, DIGEST, FORM, or CLIENT-CERT.
     */
    private $authMethod = null;

    public function getAuthMethod() {
        return ($this->authMethod);
    }

    public function setAuthMethod($authMethod) {
        $this->authMethod = $authMethod;
    }


    /**
     * The context-relative URI of the error page for form login.
     */
    private $errorPage = null;

    public function getErrorPage() {
        return ($this->errorPage);
    }

    public function setErrorPage($errorPage) {
        $this->errorPage = urldecode($errorPage);
    }


    /**
     * The context-relative URI of the login page for form login.
     */
    private $loginPage = null;

    public function getLoginPage() {
        return ($this->loginPage);
    }

    public function setLoginPage($loginPage) {
        $this->loginPage = urldecode($loginPage);
    }


    /**
     * The realm name used when challenging the user for authentication
     * credentials.
     */
    private $realmName = null;

    public function getRealmName() {
        return ($this->realmName);
    }

    public function setRealmName($realmName) {
        $this->realmName = $realmName;
    }


    // --------------------------------------------------------- Public Methods


    /**
     * Return a function representation of this object.
     */
    public function __toString() {

        $sb = "LoginConfig[";
        $sb .= "authMethod=";
        $sb .= $this->authMethod;
        if ($this->realmName != null) {
            $sb .= ", realmName=";
            $sb .= $this->realmName;
        }
        if ($this->loginPage != null) {
            $sb .= ", loginPage=";
            $sb .= $this->loginPage;
        }
        if ($this->errorPage != null) {
            $sb .= ", errorPage=";
            $sb .= $this->errorPage;
        }
        $sb .= "]";
        return ($sb);

    }

}


/**
 * Representation of a web resource collection for a web application's security
 * constraint, as represented in a <code>&lt;web-resource-collection&gt;</code>
 * element in the deployment descriptor.
 * <p>
 * <b>WARNING</b>:  It is assumed that instances of this class will be created
 * and modified only within the context of a single thread, before the instance
 * is made visible to the remainder of the application.  After that, only read
 * access is expected.  Therefore, none of the read and write access within
 * this class is synchronized.
 */
final class SecurityCollection {


    // ----------------------------------------------------------- Constructors

    /**
     * Construct a new security collection instance with specified values.
     *
     * @param name Name of this security collection
     * @param description Description of this security collection
     */
    public function __construct($name = null, $description = null) {

        $this->setName($name);
        $this->setDescription($description);

    }


    // ----------------------------------------------------- Instance Variables


    /**
     * Description of this web resource collection.
     */
    private $description = null;


    /**
     * The HTTP methods covered by this web resource collection.
     */
    private $methods = array();


    /**
     * The name of this web resource collection.
     */
    private $name = null;


    /**
     * The URL patterns protected by this security collection.
     */
    private $patterns = array();


    // ------------------------------------------------------------- Properties


    /**
     * Return the description of this web resource collection.
     */
    public function getDescription() {

        return ($this->description);

    }


    /**
     * Set the description of this web resource collection.
     *
     * @param description The new description
     */
    public function setDescription($description) {

        $this->description = $description;

    }


    /**
     * Return the name of this web resource collection.
     */
    public function getName() {

        return ($this->name);

    }


    /**
     * Set the name of this web resource collection
     *
     * @param name The new name
     */
    public function setName($name) {

        $this->name = $name;

    }


    // --------------------------------------------------------- Public Methods


    /**
     * Add an HTTP request method to be part of this web resource collection.
     */
    public function addMethod($method) {

        if ($method === null)
            return;
        $this->methods[] = $method;

    }


    /**
     * Add a URL pattern to be part of this web resource collection.
     */
    public function addPattern($pattern) {

        if ($pattern === null)
            return;
        $pattern = urldecode($pattern);
        $this->patterns[] = $pattern;

    }


    /**
     * Return <code>true</code> if the specified HTTP request method is
     * part of this web resource collection.
     *
     * @param method Request method to check
     */
    public function findMethod($method) {

        if (count($this->methods) == 0)
            return (true);
        return in_array($method, $this->methods);

    }


    /**
     * Return the set of HTTP request methods that are part of this web
     * resource collection, or a zero-length array if all request methods
     * are included.
     */
    public function findMethods() {

        return ($this->methods);

    }


    /**
     * Is the specified pattern part of this web resource collection?
     *
     * @param pattern Pattern to be compared
     */
    public function findPattern($pattern) {

        return in_array($pattern, $this->patterns);

    }


    /**
     * Return the set of URL patterns that are part of this web resource
     * collection.  If none have been specified, a zero-length array is
     * returned.
     */
    public function findPatterns() {

        return ($this->patterns);

    }


    /**
     * Remove the specified HTTP request method from those that are part
     * of this web resource collection.
     *
     * @param method Request method to be removed
     */
    public function removeMethod($method) {

        if ($method === null)
            return;

        for ($i = 0; $i < count($this->methods); $i++) {
            if ($this->methods[$i] == $method) {
                unset($this->methods[$i]);
                return;
            }
        }

    }


    /**
     * Remove the specified URL pattern from those that are part of this
     * web resource collection.
     *
     * @param pattern Pattern to be removed
     */
    public function removePattern($pattern) {

        if (pattern === null)
            return;

        for ($i = 0; $i < count($this->patterns); $i++) {
            if ($this->patterns[$i] == $pattern) {
                unset($this->patterns[$i]);
                return;
            }
        }

    }


    /**
     * Return a $representation of this security collection.
     */
    public function __toString() {

        $sb = "SecurityCollection[";
        $sb .= $this->name;
        if ($this->description !== null) {
            $sb .= ", ";
            $sb .= $this->description;
        }
        $sb .= "]";
        return ($sb);

    }

}


/**
 * Representation of a security constraint element for a web application,
 * as represented in a <code>&lt;security-constraint&gt;</code> element in the
 * deployment descriptor.
 * <p>
 * <b>WARNING</b>:  It is assumed that instances of this class will be created
 * and modified only within the context of a single thread, before the instance
 * is made visible to the remainder of the application.  After that, only read
 * access is expected.  Therefore, none of the read and write access within
 * this class is synchronized.
 */
final class SecurityConstraint {


    // ----------------------------------------------------------- Constructors


    /**
     * Construct a new security constraint instance with default values.
     */
    public function __construct() {

    }


    // ----------------------------------------------------- Instance Variables


    /**
     * Was the "all roles" wildcard included in the authorization constraints
     * for this security constraint?
     */
    private $allRoles = false;


    /**
     * Was an authorization constraint included in this security constraint?
     * This is necessary to distinguish the case where an auth-constraint with
     * no roles (signifying no direct access at all) was requested, versus
     * a lack of auth-constraint which implies no access control checking.
     */
    private $authConstraint = false;


    /**
     * The set of roles permitted to access resources protected by this
     * security constraint.
     */
    private $authRoles = array();


    /**
     * The set of web resource collections protected by this security
     * constraint.
     */
    private $collections = array();


    /**
     * The user data constraint for this security constraint.  Must be NONE,
     * INTEGRAL, or CONFIDENTIAL.
     */
    private $userConstraint = "NONE";


    // ------------------------------------------------------------- Properties


    /**
     * Was the "all roles" wildcard included in this authentication
     * constraint?
     */
    public function getAllRoles() {

        return ($this->allRoles);

    }


    /**
     * Return the authorization constraint present flag for this security
     * constraint.
     */
    public function getAuthConstraint() {

        return ($this->authConstraint);

    }


    /**
     * Set the authorization constraint present flag for this security
     * constraint.
     */
    public function setAuthConstraint($authConstraint) {

        $this->authConstraint = $authConstraint;

    }


    /**
     * Return the user data constraint for this security constraint.
     */
    public function getUserConstraint() {

        return ($this->userConstraint);

    }


    /**
     * Set the user data constraint for this security constraint.
     *
     * @param userConstraint The new user data constraint
     */
    public function setUserConstraint($userConstraint) {

        if ($userConstraint !== null)
            $this->userConstraint = $userConstraint;

    }


    // --------------------------------------------------------- Public Methods


    /**
     * Add an authorization role, which is a role name that will be
     * permitted access to the resources protected by this security constraint.
     *
     * @param authRole Role name to be added
     */
    public function addAuthRole($authRole) {

        if ($authRole === null)
            return;
        if ("*" == $authRole) {
            $this->allRoles = true;
            return;
        }
        $this->authRoles[] = $authRole;
        $this->authConstraint = true;
    }


    /**
     * Add a new web resource collection to those protected by this
     * security constraint.
     *
     * @param collection The new web resource collection
     */
    public function addCollection(SecurityCollection $collection) {

        if ($collection === null)
            return;

        $this->collections[] = $collection;

    }


    /**
     * Return <code>true</code> if the specified role is permitted access to
     * the resources protected by this security constraint.
     *
     * @param role Role name to be checked
     */
    public function findAuthRole($role) {

        if ($role === null)
            return (false);
        return in_array($role, $roles);

    }


    /**
     * Return the set of roles that are permitted access to the resources
     * protected by this security constraint.  If none have been defined,
     * a zero-length array is returned (which implies that all authenticated
     * users are permitted access).
     */
    public function findAuthRoles() {

        return ($this->authRoles);

    }


    /**
     * Return the web resource collection for the specified name, if any;
     * otherwise, return <code>null</code>.
     *
     * @param name Web resource collection name to return
     */
    public function findCollection($name) {

        if ($name === null)
            return (null);
        foreach($this->collections as $collection)
            if($collection->getName() == $name)
                return $collection;
        return null;

    }


    /**
     * Return all of the web resource collections protected by this
     * security constraint.  If there are none, a zero-length array is
     * returned.
     */
    public function findCollections() {

        return ($this->collections);

    }


    /**
     * Return <code>true</code> if the specified context-relative URI (and
     * associated HTTP method) are protected by this security constraint.
     *
     * @param uri Context-relative URI to check
     * @param method Request method being used
     */
    public function included($uri, $method) {

        // We cannot match without a valid request method
        if ($method === null)
            return (false);

        // Check all of the collections included in this constraint
        for ($i = 0; $i < count($this->collections); $i++) {
            if (!$this->collections[$i]->findMethod($method))
                continue;
            $patterns = $this->collections[$i]->findPatterns();
            for ($j = 0; $j < count($patterns); $j++) {
                if ($this->matchPattern($uri, $patterns[$j]))
                    return (true);
            }
        }

        // No collection included in this constraint matches this request
        return (false);

    }


    /**
     * Remove the specified role from the set of roles permitted to access
     * the resources protected by this security constraint.
     *
     * @param authRole Role name to be removed
     */
    public function removeAuthRole($authRole) {

        if ($authRole === null)
            return;

        for ($i = 0; $i < count($this->authRoles); $i++) {
            if ($this->authRoles[$i] == $authRole) {
                unset($this->authRoles[$i]);
                return;
            }
        }

    }


    /**
     * Remove the specified web resource collection from those protected by
     * this security constraint.
     *
     * @param collection Web resource collection to be removed
     */
    public function removeCollection(SecurityCollection $collection) {

        if ($collection == null)
            return;

        for ($i = 0; $i < count($this->collections); $i++) {
            if ($this->collections[$i] == $collection) {
                unset($this->collections[$i]);
                return;
            }
        }

    }


    /**
     * Return a String representation of this security constraint.
     */
    public function __toString() {

        $sb = "SecurityConstraint[";
        for ($i = 0; $i < count($this->collections); $i++) {
            if ($i > 0)
                $sb . ", ";
            $sb .= $this->collections[$i]->getName();
        }
        $sb .= "]";
        return ($sb);

    }


    // -------------------------------------------------------- Private Methods


    /**
     * Does the specified request path match the specified URL pattern?
     * This method follows the same rules (in the same order) as those used
     * for mapping requests to servlets.
     *
     * @param path Context-relative request path to be checked
     *  (must start with '/')
     * @param pattern URL pattern to be compared against
     */
    private function matchPattern($path, $pattern) {

        // Normalize the argument strings
        if (($path === null) || (strlen($path) == 0))
            $path = "/";
        if (($pattern === null) || (strlen($pattern) == 0))
            $pattern = "/";

        // Check for exact match
        if ($path == $pattern)
            return (true);

        // Check for path prefix matching
        if (preg_match("/^\//", $pattern) && preg_match("/\/\*$/", $pattern)) {
            $pattern = substr($pattern, 0, strlen($pattern) - 2);
            if (strlen($pattern) == 0)
                return (true);  // "/*" is the same as "/"
            if (preg_match("/\/$/", $path))
                $path = substr($path, 0, strlen($path) - 1);
            while (true) {
                if ($pattern == $path)
                    return (true);
                $slash = strrpos($path, "/");
                if (!$slash || $slash === 0)
                    break;
                $path = substr($path, 0, $slash);
            }
            return (false);
        }

        // Check for suffix matching
        if (preg_match("/^\*\./", $pattern)) {
            $slash = strrpos($path, "/");
            $period = strrpos($path, ".");
            if (($slash >= 0) && ($period > $slash) &&
                preg_match("/".substr($pattern, 1)."/", $path)) {
                return (true);
            }
            return (false);
        }

        // Check for universal mapping
        if ($pattern == "/")
            return (true);

        return (false);

    }

}

?>